<?php

/**
 * @user magein
 * @date 2023/12/30 9:46
 */

namespace magein\utils;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Psr7\Response;

class Http
{
    /**
     * @var string
     */
    protected string $type = '';

    /**
     * @var string
     */
    protected string $method = 'GET';

    /**
     * @var array
     */
    protected array $params = [];

    /**
     * @var array
     */
    protected array $headers = [];

    protected array $options = [];

    /**
     * @var Response|null
     */
    protected ?Response $response = null;

    /**
     * @var GuzzleException|null
     */
    protected ?GuzzleException $exception = null;

    /**
     * @var Client|null
     */
    protected $client = null;

    public function __construct()
    {
        $this->client = new Client();
    }

    /**
     * @param string $url
     * @return Result
     */
    protected function request(string $url): Result
    {
        if (empty($url)) {
            return Result::error('请输入请求地址');
        }

        $headers = $this->headers;

        if (empty($headers['User-Agent'])) {
            $headers['User-Agent'] = BrowserAgent::random();
        }

        $options = [
            'headers' => $headers,
            $this->type => $this->params,
        ];

        $options = array_merge($this->options, $options);

        $message = '';
        $response = null;
        /**
         * 将消息正文存储在使用 PHP 临时流的 Stream 中
         * 通过$response->getBody()->getContents()获取后将清空 Stream,再次调用后得到的是空值
         * 如果想要获取需要调用rewind()
         * $response->getBody()->rewind();
         */
        try {
            $response = $this->client->request(strtoupper($this->method ?: 'GET'), $url, $options);
            $this->response = $response;
        } catch (RequestException|ServerException $exception) {
            $this->response = $exception->getResponse();
        }

        if ($response instanceof Response) {
            return $this->response($response);
        }

        return Result::error($message ?: 'http请求异常');
    }

    /**
     * @param Response $response
     * @return Result
     */
    protected function response(Response $response): Result
    {
        if ($response->getStatusCode() !== 200) {
            return Result::error('http请求状态异常', $response->getStatusCode());
        }

        $content = $response->getBody()->getContents();
        if ($content) {
            $content = json_decode($content, true);
        }

        if (empty($content)) {
            return Result::error('接口异常', $response->getStatusCode());
        }

        return Result::success($content);
    }

    /**
     * @return Response|null
     */
    public function getResponse(): ?Response
    {
        return $this->response;
    }

    /**
     * @param $options
     * @return $this
     */
    public function setOptions($options): Http
    {
        $this->options = $options;

        return $this;
    }

    /**
     * @param $headers
     * @return $this
     */
    public function setHeaders($headers): Http
    {
        $this->headers = $headers;

        return $this;
    }

    /**
     * @param string $key
     * @param string $value
     * @return $this
     */
    public function setHeader(string $key, string $value): Http
    {
        $this->headers[$key] = $value;

        return $this;
    }

    /**
     * https://guzzle-cn.readthedocs.io/zh-cn/latest/request-options.html#multipart
     * @param string $url
     * @param array $params
     * @return Result
     */
    public function multipartFormData(string $url, array $params = []): Result
    {
        $this->method = 'POST';
        $this->params = $params;
        $this->type = 'multipart';
        return $this->request($url);
    }

    /**
     * https://guzzle-cn.readthedocs.io/zh-cn/latest/request-options.html#form-params
     * @param string $url
     * @param array $params
     * @return Result
     */
    public function formParams(string $url, array $params = []): Result
    {
        $this->method = 'POST';
        $this->params = $params;
        $this->type = 'form_params';
        return $this->request($url);
    }

    /**
     * https://guzzle-cn.readthedocs.io/zh-cn/latest/request-options.html#query
     * @param string $url
     * @param array $params
     * @return Result
     */
    public function get(string $url, array $params = []): Result
    {
        $this->method = 'GET';
        $this->params = $params;
        $this->type = 'query';
        return $this->request($url);
    }

    /**
     * https://guzzle-cn.readthedocs.io/zh-cn/latest/request-options.html#body
     * @param string $url
     * @param array $params
     * @param $method
     * @return Result
     */
    public function body(string $url, array $params = [], $method = null): Result
    {
        $this->method = $method ?: 'POST';
        $this->params = $params;
        $this->type = 'body';
        return $this->request($url);
    }

    /**
     * https://guzzle-cn.readthedocs.io/zh-cn/latest/request-options.html#json
     * @param string $url
     * @param array $params
     * @param $method
     * @return Result
     */
    public function json(string $url, array $params = [], $method = null): Result
    {
        $this->method = $method ?: 'PUT';
        $this->params = $params;
        $this->type = 'json';
        return $this->request($url);
    }
}